/*
 * Copyright (c) 2013 WDCY Information Technology Co. Ltd
 * www.wdcy.cc
 * All rights reserved.
 *
 * This software is the confidential and proprietary information of
 * WDCY Information Technology Co. Ltd ("Confidential Information").
 * You shall not disclose such Confidential Information and shall use
 * it only in accordance with the terms of the license agreement you
 * entered into with WDCY Information Technology Co. Ltd.
 */
package com.monkey.web.platform.service.impl;

import com.monkey.core.domain.auth.UserResourcePermission;
import com.monkey.core.domain.auth.UserResourcePermissionRepository;
import com.monkey.core.domain.auth.UserRoleRepository;
import com.monkey.core.domain.organization.Organization;
import com.monkey.core.domain.organization.OrganizationRepository;
import com.monkey.core.domain.permission.Permission;
import com.monkey.core.domain.permission.PermissionRepository;
import com.monkey.core.domain.resource.Menu;
import com.monkey.core.domain.role.Role;
import com.monkey.core.domain.user.User;
import com.monkey.core.domain.user.UserRepository;
import com.monkey.core.domain.user.UserStatus;
import com.monkey.core.exception.MonkeyException;
import com.monkey.core.plugin.dto.MonkeyDto;
import com.monkey.core.service.BaseServiceImpl;
import com.monkey.core.web.response.MonkeyResponseDto;
import com.monkey.utils.password.PasswordEncoder;
import com.monkey.web.dto.request.user.ModifyUserPasswordDto;
import com.monkey.web.dto.response.user.LoadOnlineUserResponseJSONDto;
import com.monkey.web.dto.response.user.OnlineUserJSONDto;
import com.monkey.web.platform.service.*;
import com.monkey.web.shiro.exception.UserBlockedException;
import com.monkey.web.shiro.exception.UserNotExistsException;
import com.monkey.web.shiro.exception.UserPasswordNotMatchException;
import com.monkey.web.shiro.mgt.AppSession;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.mgt.DefaultSessionManager;
import org.apache.shiro.session.mgt.eis.SessionDAO;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.support.DefaultSubjectContext;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;

/**
 * @author Wu Tianqiang
 */
@Service
public class UserServiceImpl extends BaseServiceImpl<User, Long> implements UserService {

    @Autowired
    private PasswordEncoder passwordEncoder;
    @Autowired
    private UserRoleRepository userRoleRepository;
    @Autowired
    private UserStatusHistoryService userStatusHistoryService;
    @Autowired
    private OrganizationRepository organizationRepository;
    @Autowired
    private PermissionRepository permissionRepository;
    @Autowired
    private UserResourcePermissionRepository userResourcePermissionRepository;
    @Autowired
    private PermissionService permissionService;
    @Autowired
    private RoleService roleService;
    @Autowired
    private MenuService menuService;

    private UserRepository getUserRepository() {
        return (UserRepository) baseRepository;
    }

    //-------------------------新增用户start--------------------------------
    public void addUser(MonkeyDto monkeyDto) {
        User user = User.createFromMonkeyDto(monkeyDto);
        validNewUser(user);

        String encryptPassword = passwordEncoder.encode(monkeyDto.getString("password"));
        user.setPassword(encryptPassword);

        Organization organization = organizationRepository.findOne(monkeyDto.getLong("orgId"));
        user.setOrganization(organization);

        save(user);
    }

    private void validNewUser(User user) {
        if (validAccountIsExist(user.getAccount())) {
            throw new MonkeyException("登录帐号已经存在");
        }
        if (validEmailIsExist(user.getEmail())) {
            throw new MonkeyException("邮箱已经存在");
        }
        if (validMobileIsExist(user.getMobile())) {
            throw new MonkeyException("移动电话已经存在");
        }
    }

    public boolean validAccountIsExist(String account) {
        User user = getUserRepository().findByAccount(account);
        return user != null;
    }

    public boolean validEmailIsExist(String email) {
        User user = getUserRepository().findByEmail(email);
        return user != null;
    }

    public boolean validMobileIsExist(String mobile) {
        User user = getUserRepository().findByMobile(mobile);
        return user != null;
    }

    //-------------------------新增用户end--------------------------------
    @Override
    public User findByAccount(String account) {
        return getUserRepository().findByAccount(account);
    }

    //=====================login() start=================================

    public User login(String account, String password) throws UserBlockedException {
        if (StringUtils.isEmpty(account) || StringUtils.isEmpty(password)) {
            throw new UserNotExistsException();
        }
        //密码如果不在指定范围内 肯定错误
        if (password.length() < User.PASSWORD_MIN_LENGTH || password.length() > User.PASSWORD_MAX_LENGTH) {
            throw new UserPasswordNotMatchException();
        }

        User user = null;
        if (maybeAccount(account)) {
            user = getUserRepository().findByAccount(account);
        }
        if (user == null && maybeEmail(account)) {
            user = getUserRepository().findByEmail(account);
        }
        if (user == null && maybeMobile(account)) {
            user = getUserRepository().findByMobile(account);
        }
//        if (user == null || Boolean.TRUE.equals(user.getDeleted())) {
        if (user == null) {
            throw new UserNotExistsException();
        }
        if (!passwordEncoder.matches(password, user.getPassword())) {
            throw new UserPasswordNotMatchException();
        }
        if (user.getStatus() == UserStatus.BLOCKED) {
            throw new UserBlockedException(userStatusHistoryService.getLastReason(user));
        }
        return user;
    }

    private boolean maybeAccount(String account) {
        if (!account.matches(User.ACCOUNT_PATTERN)) {
            return false;
        }
        //如果用户名不在指定范围内也是错误的
        return !(account.length() < User.ACCOUNT_MIN_LENGTH || account.length() > User.ACCOUNT_MAX_LENGTH);

    }

    private boolean maybeEmail(String username) {
        return username.matches(User.EMAIL_PATTERN);
    }

    private boolean maybeMobile(String username) {
        return username.matches(User.MOBILE_PATTERN);
    }
    //=====================login() end=================================

    public MonkeyResponseDto onlineUsers() {
        SessionDAO sessionDAO = loadSessionDAO();
        Collection<Session> sessions = sessionDAO.getActiveSessions();
        LoadOnlineUserResponseJSONDto responseDto = new LoadOnlineUserResponseJSONDto();
        for (Session session : sessions) {
            OnlineUserJSONDto onlineUserJSONDto = loadOnlineUser((AppSession) session);
            responseDto.addOnlineUser(onlineUserJSONDto);
        }
        return responseDto;
    }

    private OnlineUserJSONDto loadOnlineUser(AppSession session) {
        PrincipalCollection principalCollection = (PrincipalCollection) session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);
        Long userId = (Long) principalCollection.getPrimaryPrincipal();
        User user = findOne(userId);
        return new OnlineUserJSONDto(session, user);
    }

    private SessionDAO loadSessionDAO() {
        DefaultWebSecurityManager securityManager = (DefaultWebSecurityManager) SecurityUtils.getSecurityManager();
        DefaultSessionManager sessionManager = (DefaultSessionManager) securityManager.getSessionManager();
        return sessionManager.getSessionDAO();
    }

    public void modifyUser(MonkeyDto monkeyDto) {
        User user = findOne(monkeyDto.getLong("id"));
        if (user.getAdmin()) {
            throw new MonkeyException("不能修改管理员帐号");
        }
        User.modifyFromMonkeyDto(user, monkeyDto);
    }

    public MonkeyResponseDto deleteUser(Long id) {
        User user = findOne(id);
        if (user.getAdmin()) {
            return new MonkeyResponseDto().fail("cannot.delete.admin");
        }
        delete(id);
        return new MonkeyResponseDto().fail("delete.user.success");
    }

    public MonkeyResponseDto modifyPassword(ModifyUserPasswordDto modifyUserPasswordDto) {
        Long userId = modifyUserPasswordDto.getUserId();
        User user = getUserRepository().findOne(userId);
        if (!passwordEncoder.matches(modifyUserPasswordDto.getOldPassword(), user.getPassword())) {
            return new MonkeyResponseDto().fail("old.password.not.match");
        }
        user.setPassword(passwordEncoder.encode(modifyUserPasswordDto.getNewPassword()));
        return new MonkeyResponseDto().success("modify.password.success");
    }


    @Override
    public void restPasswd(MonkeyDto monkeyDto) {
        String newPwd = monkeyDto.getString("newPwd");
        String newPwd2 = monkeyDto.getString("newPwd2");
        if (!newPwd.equals(newPwd2)) {
            throw new MonkeyException("两次输入密码不一致");
        }
        User user = findOne(monkeyDto.getLong("userId"));
        String encryptPassword = passwordEncoder.encode(newPwd);
        user.setPassword(encryptPassword);

        update(user);
    }

    @Override
    public void changeStatus(Long[] ids, UserStatus status, String reason, User opUser) {
        for (Long id : ids) {
            User user = findOne(id);
            user.setStatus(status);
            update(user);
            userStatusHistoryService.log(opUser, user, status, reason);
        }
    }

    @Override
    public void grantPermissionToUser(User user, Menu menu, Long[] permissionIds) {
        for (Long permissionId : permissionIds) {
            UserResourcePermission urp = new UserResourcePermission();
            urp.setUser(user);
            urp.setMenu(menu);
            Permission permission = permissionRepository.findOne(permissionId);
            urp.setPermission(permission);
            userResourcePermissionRepository.save(urp);
        }
    }

    @Override
    public void recoveryPermissionFromUser(User user, Menu menu, Long[] permissionIds) {
        for (Long permissionId : permissionIds) {
            Permission permission = permissionRepository.findOne(permissionId);
            userResourcePermissionRepository.deleteByUserAndMenuAndPermission(user, menu, permission);
        }
    }

    @Override
    public List<Permission> getPermissionByUserMenu(User user, Menu menu) {
        List<UserResourcePermission> rrps = userResourcePermissionRepository.findByUserAndMenu(user, menu);
        List<Permission> permissions = new ArrayList<>();
        for (UserResourcePermission rrp : rrps) {
            permissions.add(rrp.getPermission());
        }
        return permissions;
    }

    @Override
    public Page<UserResourcePermission> getPermissionByUser(User user, Pageable pageable) {
        Page<UserResourcePermission> urps = userResourcePermissionRepository.findByUser(user, pageable);
        for (UserResourcePermission rup : urps.getContent()) {
            String identity = menuService.findActualMenuIdentity(rup.getMenu());
            rup.setActualIdentity(identity + ":" + rup.getPermission().getPermission());
        }
        return urps;
    }

    @Override
    public List<Role> getRolesByUser(User user) {
        return userRoleRepository.findByUser(user);
    }

    @Override
    public Set<String> findPermissions(Long userId) {
        Set<String> permissions = permissionService.findStringPermissions(userId);
        Set<String> roles = roleService.findStringRoles(userId);
        for (String role : roles) {
            permissions.addAll(roleService.resolvePermissionsInRole(role));
        }
        return permissions;
    }


}